package com.example.mykotlin.base.util

import cn.dev33.satoken.stp.StpUtil
import com.example.mykotlin.base.interceptor.RequestInfoInterceptor
import com.example.mykotlin.base.result.R
import com.example.mykotlin.model.entity.AuditLog
import com.example.mykotlin.model.pojo.AuditLogDataInfo
import com.mybatisflex.kotlin.extensions.db.insert
import io.swagger.v3.oas.annotations.Operation
import java.net.InetAddress
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.springframework.stereotype.Component

@Aspect
@Component
class AuditLogAspect {

    companion object {
        val AUDIT_LOG_DATA_INFO = ThreadLocal<AuditLogDataInfo>()
    }

    // 切点`
    @Pointcut("@annotation(com.example.mykotlin.base.util.Audit)")
    fun executePointCut() = Unit


    // around 切面强化，用于捕获和处理方法调用
    @Around("executePointCut()")
    @Throws(Exception::class)
    fun execute(joinPoint: ProceedingJoinPoint): Any? {
        // 创建一个AuditLogDataInfo实例
        val auditLogDataInfo = AuditLogDataInfo()
        // 将AuditLogDataInfo设置到全局变量中
        AUDIT_LOG_DATA_INFO.set(auditLogDataInfo)

        // 获取方法调用参数
        val args = joinPoint.args
        // 创建一个AuditLog实例
        val auditLog = AuditLog()
        tryOnly {
            // 获取方法调用目标类
            val controllerClass = joinPoint.target.javaClass
            // 获取方法调用方法名
            val methodName = joinPoint.signature.name
            // 获取方法调用参数类型
            val argsClass = Array(args.size) { args[it].javaClass }
            // 获取方法调用方法
            val method = controllerClass.getMethod(methodName, *argsClass)
            val requestInfo = RequestInfoInterceptor.REQUEST_INFO.get() ?: Triple("", "", "")
            // 设置AuditLog的apiId
            auditLog.apiId = requestInfo.third + requestInfo.second.replace("/", "_")
            // 设置AuditLog的apiName
            auditLog.apiName = tryReturn("") { method.getAnnotation(Operation::class.java).summary }
            // 设置AuditLog的userId
            auditLog.userId = tryReturn(0L) { StpUtil.getLoginIdAsLong() }
            // 设置AuditLog的username
            auditLog.username = tryReturn("") { StpUtil.getSession().get("username").toString() }
            // 设置AuditLog的ipAddress
            auditLog.ipAddress = requestInfo.first
            // 设置AuditLog的serviceIpAddress
            auditLog.serviceIpAddress = tryReturn("") { InetAddress.getLocalHost().hostAddress ?: "" }
        }

        try {
            // 执行方法调用
            val proceed = joinPoint.proceed(args)
            tryOnly {
                // 如果方法调用结果是R类型且flag为true
                if (proceed is R<*> && proceed.flag == true) {
                    // 遍历AuditLogDataInfo的finalAllChangeData
                    for (logData in auditLogDataInfo.finalAllChangeData) {
                        // 获取表名
                        val table = logData.table ?: ""
                        // 遍历insertData
                        for ((_, value) in auditLogDataInfo.insertData[table]!!) {
                            // 添加insert值到logData中
                            logData.insert.add(value)
                        }
                        // 遍历oldUpdateData
                        for ((_, value) in auditLogDataInfo.oldUpdateData[table]!!) {
                            // 添加oldUpdate值到logData中
                            logData.oldDataOfUpdate.add(value)
                        }
                        // 遍历newUpdateData
                        for ((_, value) in auditLogDataInfo.newUpdateData[table]!!) {
                            // 添加newUpdate值到logData中
                            logData.newDataOfUpdate.add(value)
                        }
                        // 遍历deleteData
                        for ((_, value) in auditLogDataInfo.deleteData[table]!!) {
                            // 添加delete值到logData中
                            logData.delete.add(value)
                        }
                    }
                    // 将AuditLog的changeValue设置为auditLogDataInfo的finalAllChangeData的JSON字符串
                    auditLog.changeValue =
                        GetBean.objectMapper().writeValueAsString(auditLogDataInfo.finalAllChangeData)
                    // 插入AuditLog
                    try {
                        insert(auditLog)
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }

                }
            }
            // 返回方法调用结果
            return proceed
        } finally {
            // 清除AuditLogDataInfo
            AUDIT_LOG_DATA_INFO.remove()
        }
    }


}